package it.tukano.app;

import it.tukano.fun.Function1;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * This module holds references to the other modules
 * @author pgi
 */
public class AppContext implements AppModule {

    /** The list of modules registerd in this context */
    private final List<AppModule> registeredModules = new CopyOnWriteArrayList<AppModule>();

    /**
     * Instance initializer
     */
    public AppContext() {
    }

    /**
     * Initializes all the modules registered in this context
     * @param context the context used to initialize the modules
     */
    @Override
    public void initModule(AppModule context) {
        for (AppModule module : registeredModules) {
            module.initModule(context);
        }
    }

    /**
     * Starts all the modules registered in this context
     * @param context the context used to start the modules
     */
    @Override
    public void startModule(AppModule context) {
        for (AppModule module : registeredModules) {
            module.startModule(this);
        }
    }

    /**
     * Stops all the modules registered in this context
     * @param context the context used to to stop the modules
     */
    @Override
    public void stopModule(AppModule context) {
        for (AppModule module : registeredModules) {
            module.stopModule(context);
        }
    }

    /**
     * Destroyes all the modules registred in this context
     * @param context the context sent to the modules
     */
    @Override
    public void destroyModule(AppModule context) {
        for (AppModule module : registeredModules) {
            module.destroyModule(context);
        }
    }

    /**
     * Finds a module among those registered with this context
     * @param <A> the type of the module to get
     * @param type the class type used to find the module
     * @return the first module of type B <: A registered in this context or null if no such module exists
     * @throws IllegalArgumentException if type is null
     */
    @Override
    public <A extends AppModule> A find(Class<A> type) {
        if(type == null) throw new IllegalArgumentException("type parameter cannot be null");
        for (AppModule module : registeredModules) {
            if(type.isAssignableFrom(module.getClass())) return type.cast(module);
        }
        return null;
    }

    /**
     * Add a set of modules to this context
     * @param modules the modules to add
     * @throws IllegalArgumentException if modules is null
     */
    public void add(Collection<? extends AppModule> modules) {
        if(modules == null) throw new IllegalArgumentException("modules parameter cannot be null");
        registeredModules.addAll(modules);
    }

    /**
     * Add a module and return this
     * @param module the module to add
     * @return this
     */
    public AppContext append(AppModule module) {
        registeredModules.add(module);
        return this;
    }

    public <A extends AppModule> void withModule(Class<A> moduleClass, Function1<A, ?> callback) {
        A moduleInstance = find(moduleClass);
        if(moduleInstance != null) {
            callback.apply(moduleInstance);
        }
    }
}
